#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
Simplified BSD License (BSD)
Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this 
      list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright notice, 
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/
#endregion

#region Record of Change
//             							CHANGE HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | DEVELOPER  | DESCRIPTION                                              |
// -------------------------------------------------------------------------------------
// | 17-Nov-09 | HaiNM      | First creation.                                          |
// -------------------------------------------------------------------------------------
//
#endregion

#region Record of Audit
//             							REVIEW HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | REVIEWER   | NOTES                                                    |
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
//
#endregion

#region Using directives
using System;
using System.Collections;

using Thn;
#endregion

namespace Thn.Interface.Vcl
{
    /// <summary>
    /// Strongly typed list of <see cref="ControlContainer"/>.
    /// </summary>    
#if DEBUG
    public
#else
    public
#endif
 class ControlContainerList : IList, ICollection, IEnumerable
    {
        #region Fields
        /// <summary>
        /// Internal data.
        /// </summary>
        internal ControlContainer[] mItems = null;
        /// <summary>
        /// Number of items currently in list.
        /// </summary>
        internal int mCount = 0;
        /// <summary>
        /// A value indicating whether access to the ICollection is synchronized (thread safe).
        /// </summary>
        [NonSerialized]
        private object mSynchRoot;
        #endregion

        #region Capacity
        private int mCapacity;
        /// <summary>
        /// Gets current maximal capacity.
        /// </summary>
        public int Capacity
        {
            get { return mCapacity; }
        }
        #endregion

        #region Host
        private IControlContainerHost mHost;
        /// <summary>
        /// Gets/Sets the host that houses this list.
        /// </summary>
        public IControlContainerHost Host
        {
            get { return mHost; }
            set { mHost = value; }
        }
        #endregion

        #region IControlContainerList Members
        #region Copy From

        #region From List
        /// <summary>
        /// Clear this list and copy contents from the source list, assuming all items in the source is castable to <see cref="Object"/>.
        /// </summary>
        /// <param name="source">The source to copy contents from.</param>
        public void CopyFrom(IList source)
        {
            CopyFrom(source, 0, source.Count);
        }

        /// <summary>
        /// Clear this list and copy contents from the source list, assuming all items in the source is castable to <see cref="Object"/>.
        /// </summary>
        /// <param name="source">The source to copy contents from.</param>
        /// <param name="index">Index of the first item to be copied.</param>
        /// <param name="count">Number of items to copy.</param>
        public void CopyFrom(IList source, int index, int count)
        {
            Clear();

            if (source is ControlContainerList)
            {
                #region Use fast copy

                ControlContainerList src = (ControlContainerList)source;

                //ensure enough capacity
                if (mItems.Length < count) mItems = new ControlContainer[count];
                #region Defensive tracing
                else
                {
                    //don't need create new instance.
                }
                #endregion
                //copy content
                Array.Copy(src.mItems, index, mItems, 0, count);
                mCount = count;

                #endregion
            }
            else
            {
                #region Use normal copy
                for (int i = index; i < count; i++)
                {
                    Add((ControlContainer)source[i]);
                }
                #endregion
            }
        }
        #endregion

        #region From Array
        /// <summary>
        /// Clear this list and copy contents from the source array, assuming all items in the source is castable to <see cref="Object"/>.
        /// </summary>
        /// <param name="source">The source to copy contents from.</param>
        public void CopyFrom(Array source)
        {
            CopyFrom(source, 0, source.Length);
        }

        /// <summary>
        /// Clear this list and copy contents from the source array, assuming all items in the source is castable to <see cref="Object"/>.
        /// </summary>
        /// <param name="source">The source to copy contents from.</param>
        /// <param name="index">Index of the first item to be copied.</param>
        /// <param name="count">Number of items to copy.</param>
        public void CopyFrom(Array source, int index, int count)
        {
            Clear();

            //ensure enough capacity
            if (mItems.Length < count) mItems = new ControlContainer[count];

            //copy content
            Array.Copy(source, index, mItems, 0, count);
            mCount = count;
        }
        #endregion

        #endregion

        #region Copy To

        #region To List
        /// <summary>
        /// Clear the target list and copy all contents from this list to the target list.
        /// </summary>
        /// <param name="target">The destination list to copy contents to.</param>
        public void CopyTo(IList target)
        {
            target.Clear();

            if (target is ControlContainerList)
            {
                #region Use fast copy
                ControlContainerList dst = (ControlContainerList)target;

                //ensure enough target's capacity
                if (dst.mItems.Length < mCount)
                {
                    dst.mItems = new ControlContainer[mCount];
                    dst.mCapacity = mCount;
                }
                #region Defensive tracing
                else
                {
                    //already enough space
                }
                #endregion

                //copy contents
                Array.Copy(mItems, dst.mItems, mCount);

                //update target's count
                dst.mCount = mCount;
                #endregion
            }
            else
            {
                #region Use normal copy
                for (int i = 0; i < mCount; i++)
                {
                    target.Add(mItems[i]);
                }
                #endregion
            }
        }
        #endregion

        #endregion

        #region Contains
        /// <summary>
        /// Determines whether the list contains a specific value.
        /// </summary>
        public bool Contains(ControlContainer value)
        {
            return ((IList)this).Contains(value);
        }
        #endregion

        #endregion

        #region IList Members
        #region Add
        /// <summary>
        /// Adds an item to the list.
        /// </summary>
        public int Add(ControlContainer value)
        {
            //check if not enough space then allocate more
            if (mCount >= mCapacity)
            {
                //calculate new capacity
                int newCapacity;
                if (mCapacity < 1000) newCapacity = (int)(mCapacity * 2);
                else if (mCapacity < 5000) newCapacity = (int)(mCapacity * 1.5);
                else newCapacity = (int)(mCapacity * 1.2);

                //allocate new buffer
                ControlContainer[] newItems = new ControlContainer[newCapacity];

                //copy old values to new buffer
                mItems.CopyTo(newItems, 0);

                //assign the new bufer to current buffer
                mItems = newItems;
                mCapacity = newCapacity;
            }
            #region Defensive tracing
            else
            {
                //not enough space then don't need to allocate more
            }
            #endregion

            //assign the new value to the end of current buffer
            mItems[mCount] = value;
            mCount++;

            //raise notification to host
            value.ParentList = this;
            if (mHost != null)
            {                
                mHost.NotifyChildAdded(this, value);
            }
            #region Defensive tracing
            else
            {
                //host is unavailable
            }
            #endregion

            return mCount - 1;
        }

        /// <summary>
        /// Adds an item to the list.
        /// </summary>
        int IList.Add(object value)
        {
            return Add((ControlContainer)value);
        }
        #endregion

        #region Clear
        /// <summary>
        /// Removes all items.
        /// </summary>
        public void Clear()
        {
            Array.Clear(mItems, 0, mCount);
            mCount = 0;

            //raise notification to host
            if (mHost != null) mHost.NotifyChildrenCleared(this);
            #region Defensive tracing
            else
            {
                //host is unavailable
            }
            #endregion
        }
        #endregion

        #region Contains
        /// <summary>
        /// Determines whether the list contains a specific value.
        /// </summary>
        bool IList.Contains(object value)
        {
            bool result = false;
            if (value == null)
            {
                //search for the first occurence of null item
                for (int i = 0; i < mCount; i++)
                {
                    if (mItems[i] == null)
                    {
                        result = true;
                        break;
                    }
                    #region Defensive tracing
                    else
                    {
                        //continue to find next item.
                    }
                    #endregion
                }
            }
            else
            {
                //search for the first match
                for (int i = 0; i < mCount; i++)
                {
                    if (value.Equals(mItems[i]))
                    {
                        result = true;
                        break;
                    }
                    #region Defensive tracing
                    else
                    {
                        //continue to find next item.
                    }
                    #endregion
                }
            }

            return result;
        }
        #endregion

        #region Index Of
        /// <summary>
        /// Determines the index of a specific item.
        /// </summary>
        public int IndexOf(ControlContainer value)
        {
            int result = -1;
            if (value == null)
            {
                //search for the first occurence of null item
                for (int i = 0; i < mCount; i++)
                {
                    if (mItems[i] == null)
                    {
                        result = i;
                        break;
                    }
                    #region Defensive tracing
                    else
                    {
                        //continue find next item.
                    }
                    #endregion
                }
            }
            else
            {
                //search for the first match
                for (int i = 0; i < mCount; i++)
                {
                    if (value.Equals(mItems[i]))
                    {
                        result = i;
                        break;
                    }
                    #region Defensive tracing
                    else
                    {
                        //continue find next item.
                    }
                    #endregion
                }
            }

            return result;
        }

        /// <summary>
        /// Determines the index of a specific item.
        /// </summary>
        int IList.IndexOf(object value)
        {
            return IndexOf((ControlContainer)value);
        }
        #endregion

        #region Insert
        /// <summary>
        /// Inserts an item to the list at the specified index.
        /// </summary>
        public void Insert(int index, ControlContainer value)
        {
            //check if not enough space then allocate more
            if (mCount >= mCapacity)
            {
                //calculate new capacity
                int newCapacity;
                if (mCapacity < 1000) newCapacity = (int)(mCapacity * 2);
                else if (mCapacity < 5000) newCapacity = (int)(mCapacity * 1.5);
                else newCapacity = (int)(mCapacity * 1.2);

                //allocate new buffer
                ControlContainer[] newItems = new ControlContainer[newCapacity];

                //copy old values from left of index to new buffer
                if (index > 0)
                {
                    Array.Copy(mItems, 0, newItems, 0, index);
                }
                #region Defensive tracing
                else
                {
                    //out of range.
                }
                #endregion

                //assign the new value to the index position of the new buffer
                newItems[index] = value;

                //copy old values from right of index to new buffer
                if (index < mCount)
                {
                    Array.Copy(mItems, index, newItems, index + 1, mCount - index);
                }
                #region Defensive tracing
                else
                {
                    //out of range.
                }
                #endregion

                //assign the new bufer to current buffer
                mItems = newItems;
                mCapacity = newCapacity;
                mCount++;
            }
            else
            {
                //copy old values from right of index to new buffer
                if (index < mCount)
                {
                    Array.Copy(mItems, index, mItems, index + 1, mCount - index);
                }
                #region Defensive tracing
                else
                {
                    //out of range.
                }
                #endregion

                //assign the new value to the index position of the new buffer
                mItems[index] = value;
                mCount++;
            }

            //raise notification to host
            value.ParentList = this;
            if (mHost != null)
            {                
                mHost.NotifyChildAdded(this, value);
            }
            #region Defensive tracing
            else
            {
                //host is unavailable
            }
            #endregion
        }

        /// <summary>
        /// Inserts an item to the list at the specified index.
        /// </summary>
        void IList.Insert(int index, object value)
        {
            Insert(index, (ControlContainer)value);
        }
        #endregion

        #region Is Fixed Size
        /// <summary>
        /// Gets a value indicating whether the list has a fixed size.
        /// </summary>
        public bool IsFixedSize
        { get { return false; } }
        #endregion

        #region Is Read Only
        /// <summary>
        /// Gets a value indicating whether the list is read-only.
        /// </summary>
        public bool IsReadOnly
        { get { return false; } }
        #endregion

        #region Remove
        /// <summary>
        /// Removes the first occurrence of a specific object from the list.
        /// </summary>
        public void Remove(ControlContainer value)
        {
            int index = IndexOf(value);
            if (index >= 0) RemoveAt(index);
            #region Defensive tracing
            else
            {
                //out of range.
            }
            #endregion
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the list.
        /// </summary>
        void IList.Remove(object value)
        {
            int index = IndexOf((ControlContainer)value);
            if (index >= 0) RemoveAt(index);
            #region Defensive tracing
            else
            {
                //out of range.
            }
            #endregion
        }
        #endregion

        #region Remove At
        /// <summary>
        /// Removes the list item at the specified index.
        /// </summary>
        public void RemoveAt(int index)
        {            
            mCount--;
            if (index < mCount)
            {
                ControlContainer value = mItems[index];

                //copy old values from the right of index
                Array.Copy(mItems, index + 1, mItems, index, mCount - index);

                //raise notification to host
                if (mHost != null)
                {
                    value.ParentList = null;
                    mHost.NotifyChildRemoved(this, value);
                }
                #region Defensive tracing
                else
                {
                    //host is unavailable
                }
                #endregion
            }
            #region Defensive tracing
            else
            {
                //out of range.
            }
            #endregion
            mItems[mCount] = null;
        }
        #endregion

        #region Indexer
        /// <summary>
        /// Gets or sets the element at the specified index.
        /// </summary>
        public ControlContainer this[int index]
        {
            get { return mItems[index]; }
            set
            {
                //unattach the previous child
                ControlContainer item = mItems[index];
                item.ParentList = null;

                //assign new value
                mItems[index] = value;
                value.ParentList = this;

                //raise notification to host
                if (mHost != null)
                {                    
                    mHost.NotifyChildRemoved(this, value);
                }
                #region Defensive tracing
                else
                {
                    //host is unavailable
                }
                #endregion
            }
        }

        /// <summary>
        /// Gets or sets the element at the specified index.
        /// </summary>
        object IList.this[int index]
        {
            get { return mItems[index]; }
            set { mItems[index] = (ControlContainer)value; }
        }
        #endregion

        #endregion

        #region ICollection Members
        #region Copy To
        /// <summary>
        /// Copies the elements of the list to an array.
        /// </summary>
        /// <param name="array">The one-dimensional array that is the destination of the elements copied from list. The array must have zero-based indexing.</param>
        public void CopyTo(Array array)
        {
            Array.Copy(mItems, 0, array, 0, mCount);
        }

        /// <summary>
        /// Copies the elements of the list to an array, starting at a particular array index. 
        /// </summary>
        /// <param name="array">The one-dimensional array that is the destination of the elements copied from list. The array must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in array at which copying begins.</param>
        public void CopyTo(Array array, int index)
        {
            Array.Copy(mItems, 0, array, index, mCount);
        }

        /// <summary>
        /// Copies the elements of the list to an array, starting at a particular array index. 
        /// </summary>
        /// <param name="array">The one-dimensional array that is the destination of the elements copied from list. The array must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in this list at which copying begins.</param>
        /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
        /// <param name="count">The number of elements to copy.</param>
        public void CopyTo(int index, Array array, int arrayIndex, int count)
        {
            Array.Copy(mItems, index, array, arrayIndex, count);
        }
        #endregion

        #region Count
        /// <summary>
        /// Gets the number of elements contained in the <see cref="ICollection"/>.
        /// </summary>
        public int Count
        { get { return mCount; } }
        #endregion

        #region Is Synchronized
        /// <summary>
        /// Gets a value indicating whether access to the ICollection is synchronized (thread safe).
        /// </summary>
        public bool IsSynchronized
        { get { return false; } }
        #endregion

        #region SyncRoot
        /// <summary>
        /// Gets an object that can be used to synchronize access to the ICollection.
        /// </summary>
        public object SyncRoot
        {
            get
            {
                if (mSynchRoot == null)
                {
                    System.Threading.Interlocked.CompareExchange(ref mSynchRoot, new object(), null);
                }
                #region Defensive tracing
                else
                {
                    //out of range.
                }
                #endregion
                return mSynchRoot;
            }
        }
        #endregion

        #endregion

        #region IEnumerable Members

        #region Get Enumerator
        /// <summary>
        /// Returns an enumerator that iterates through this list.
        /// </summary>
        public IEnumerator GetEnumerator()
        {
            return new ControlContainerListEnumerator(this);
        }

        #region Enumerator
        /// <summary>
        /// Strongly typed enumerator of ControlContainer.
        /// </summary>
        public class ControlContainerListEnumerator : object, System.Collections.IEnumerator
        {
            #region Fields
            /// <summary>
            /// Current index.

            /// </summary>
            private int mIndex;

            /// <summary>
            /// Current element pointed to.
            /// </summary>
            private ControlContainer mCurrentElement;

            /// <summary>
            /// List to enumerate.
            /// </summary>
            private ControlContainerList mCollection;
            #endregion

            #region Properties
            #region Current
            /// <summary>
            /// Gets the ControlContainer object in the enumerated ControlContainerList currently indexed by this instance.
            /// </summary>
            public ControlContainer Current
            { get { return mCurrentElement; } }

            /// <summary>
            /// Gets the current element in the list.
            /// </summary>
            object System.Collections.IEnumerator.Current
            { get { return mCurrentElement; } }
            #endregion
            #endregion

            #region Reset
            /// <summary>
            /// Reset the cursor, so it points to the beginning of the enumerator.
            /// </summary>
            public void Reset()
            {
                mIndex = -1;
                mCurrentElement = null;
            }
            #endregion

            #region Move Next
            /// <summary>
            /// Advances the enumerator to the next queue of the enumeration, if one is currently available.
            /// </summary>
            /// <returns>true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration.</returns>
            public bool MoveNext()
            {
                mIndex++;
                if (mIndex < mCollection.mCount)
                {
                    mCurrentElement = mCollection.mItems[mIndex];
                    return true;
                }
                #region Defensive tracing
                else
                {
                    //out of range.
                }
                mIndex = mCollection.mCount;
                return false;
            }
                #endregion

            #region Constructor
            /// <summary>
            /// Default constructor for enumerator.
            /// </summary>
            /// <param name="list">Instance of the list to enumerate.</param>
            internal ControlContainerListEnumerator(ControlContainerList list)
            {
                mIndex = -1;
                mCollection = list;
            }
            #endregion
        }
            #endregion

        #endregion

        #endregion

        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// <para>Default capacity is 50.</para>
        /// </summary>
        public ControlContainerList()
        {
            mCapacity = 50;
            mItems = new ControlContainer[mCapacity];
        }

        /// <summary>
        /// Create a new instance with the specified capacity.
        /// </summary>
        public ControlContainerList(int capacity)
        {
            mCapacity = capacity;
            mItems = new ControlContainer[capacity];
        }
        #endregion
    }
}
